import { oStorage } from "../Project";
import { INode } from "./node/instance";
import lodash from 'lodash'
import { ISlot } from "./slot/instance";
import { PSlot } from "./slot/prototype";
import { PNodeType } from "./node/type";
import type { VarInit } from "~/models/data/var_init";
import type { IIdentified } from "~/models/Project/Project";
import type { IDeserializable } from "~/models/serialize/IDeserializable";
import { PNode } from "./node/prototype";

/**
 * Trainer Graph Instance
 */
export class Graph implements IIdentified, IDeserializable {

  id: string = randomName()
  className: string = 'Graph'


  // 局部变量
  locals: VarInit[] = []

  nodesId: string[] = []
  get nodes(): INode[] {
    return this.nodesId.map(x => oStorage.value!.nodes.find(y => x === y.id)!)
  }

  /**
   * 参数
   * 
   * 1. 作为传入的变量，进行读取
   * 2. 蓝图作为节点调用时，传入参数
   */
  params: PSlot[] = []

  /**
   * 返回值
   * 
   * 1. 作为出的变量，进行写入
   * 2. 蓝图作为节点调用时，传出返回值
   */
  returns: PSlot[] = []

  constructor(
    public name: string = '',
    public title: string = '',
    mount = false
  ) {
    if (mount) {
      oStorage.value!.graphs.push(this)
    }
  }

  /**
   * 修复开始结束节点
   */
  fixProcess() {
    let start = this.nodes.find(x => x.prototype.name === 'Start')
    let end = this.nodes.find(x => x.prototype.name === 'End')
    let nCreated = 0

    // 修复Start节点
    if (!start) {
      start = new INode(PNode.Process.Start, true)
      start.x = 100; start.y = 100
      this.nodesId.push(start.id)
      nCreated += 1
    }

    // 修复End节点
    if (!end) {
      end = new INode(PNode.Process.End, true)
      end.x = 500; end.y = 100
      this.nodesId.push(end.id)
      nCreated += 1
    }

    // 如果是两个节点都新生成，就顺便连上
    if (nCreated == 2) {
      start.getProcessOut()?.connect(end.getProcessIn())
    }

    // 用来对比槽列表，进行稳定性（不变不替换）覆盖的方法
    const compareAndUpdate = (oldList: ISlot[], newList: ISlot[], sourceList: string[]) => {
      for (let i=0;i<newList.length;i++) {
        const origin = oldList[i]
        const shouldBe = newList[i]
        if (!origin || !origin.prototype.same(shouldBe.prototype)) {
          if (origin) {
            origin.disconnect()
          }
          sourceList[i] = shouldBe.id
          console.log('发生替换');
        }
        else {
          console.log('跳过', origin.prototype, shouldBe.prototype);
        }
      }
      // 截断
      if (sourceList.length > newList.length) {
        sourceList = sourceList.slice(0, newList.length)
      }
      return sourceList
    }

    // 修复Start的槽
    const startslotsShouldBe = [
      new ISlot(PSlot.Preset.ProcessOut, true),
      ...this.params.map(x => {
        const ret = new ISlot(x.createMirror(), true)
        delete ret.data
        return ret
      })
    ]
    
    start.slotsId = compareAndUpdate(start.slots, startslotsShouldBe, start.slotsId)


    // 修复End的槽
    const endslotsShouldBe = [
      new ISlot(PSlot.Preset.ProcessIn, true),
      ...this.returns.map(x => new ISlot(x.createMirror(), true))
    ]
    end.slotsId = compareAndUpdate(end.slots, endslotsShouldBe, end.slotsId)
  }

  // 获取开始节点
  public getStartNode(): INode | undefined {
    for (const node of this.nodes) {
      const p = node.prototype
      if (
        p.type === PNodeType.Process
        && p.name === 'Start'
      ) {
        return node
      }
    }
    return undefined
  }

  /**
   * 
   * REGION SERILAZE
   * 以下区域负责图的序列化
   * 
   */

  // 图的序列化中，最重要的是把节点之间的连接序列化，因为他们之间是双向关系。
  public serialize(): any {

    const that = lodash.cloneDeep(this)

    // 生成随机字符串作为id
    function generateRandomString() {
      var result = '';
      var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
      var charactersLength = characters.length;
      for (var i = 0; i < 16; i++) {
      result += characters.charAt(Math.floor(Math.random() * charactersLength));
      }
      return result;
    }

    // 所有slot
    const allSlot: ISlot[] = []
    that.nodes.forEach(node => {
      node.slots.forEach(slot => {
        allSlot.push(slot)
      })
    })

    // 为每个slot生成id字段
    const mapSlotId = new Map<ISlot, string>()
    allSlot.forEach(slot => {
      const id = generateRandomString()
      ;(slot as any).id__ = id
      mapSlotId.set(slot, id)
    })

    // 把每个slot的peer替换为peer的id
    allSlot.forEach(slot => {
      ;(slot as any).peersid__ = slot.peers.map(p => mapSlotId.get(p))
      slot.disconnect()
    })

    return JSON.parse(JSON.stringify(that))
  }

  /**
   * 
   * REGION SERILAZE END
   * 
   */
  
}

/**
 * oGraph - opened Graph
 * 打开的图象
 */
export const oGraph = ref(null as Graph | null)